package model

import (
	"encoding/base64"
	"ginblog/errmsg"
	_ "github.com/go-playground/validator/v10"
	"github.com/jinzhu/gorm"
	"golang.org/x/crypto/scrypt"
	"log"
)

// User 定义模型
type User struct {
	/*
		使用了GORM框架中的type:varchar(20);not null指定了该字段在数据库中的类型为VARCHAR(20)并且不能为NULL。
		该字段同时在JSON序列化和反序列化时使用了json:"username"标签，表示JSON对象中该字段对应的属性名为username。
		该字段还使用了 binding:"required,regexp=^[a-zA-Z\u4e00-\u9fa5]{2,20}$"`来验证输入是否为人名是汉字或字母
		该字段还使用了binding:"required,min=6,max=12"来验证输入的密码长度
		该字段还使用了binding:"required,oneof=0 1来验证输入的角色 0 是管理员  1是普通用户
	*/
	gorm.Model
	Username string `gorm:"type:varchar(20);not null" json:"username" binding:"required,username"`
	Password string `gorm:"type:varchar(20);not null" json:"password"  binding:"required,password"`
	Role     int    `gorm:"type:int;not null" json:"role" binding:"required,RoleValidator"`
}

// CheckUser 检查用户是否存在
func CheckUser(name string) int {
	var users User
	Db.Select("id").Where("username=?", name).First(&users)
	if users.ID > 0 {
		return errmsg.ErrorUsernameUsed
	}
	return errmsg.SUCCESS
}

// CreateUser 添加用户 返回一个code ，根据code去errmsg得到对应的数值
func CreateUser(data *User) int {
	//将加盐后的密码写进去
	/*
				方法一、在数据进去数据库前直接加加密(此处采用的是第一种)
				方法二、采用钩子函数
			func (u *User) BeforeSave(){
				u.Password=ScryptPwd(u.Password)
			}
			func ScryptPwd(password string) string {
			const PwdHashbyte = 10
			salt := make([]byte, 8)
			salt = []byte{15, 78, 32, 90, 59, 25, 32, 67}
			HashPwd, err := scrypt.Key([]byte(password), salt, 16384, 8, 1, PwdHashbyte)
			if err != nil {
				log.Fatal(err)
			}
			FPwd := base64.StdEncoding.EncodeToString(HashPwd)
			return FPwd //这是加密后的密码
		}

	*/
	data.Password = ScryptPwd(data.Password)
	err := Db.Create(&data).Error
	if err != nil {
		return errmsg.ERROR
	}
	return errmsg.SUCCESS
}

// ViewById 查看单个用户
func ViewById(id int, data *User) int {
	Err = Db.Where("id=?", id).Find(&data, id).Error
	if Err != nil {
		return errmsg.ERROR
	}
	if int(data.ID) == id {
		return errmsg.SUCCESS
	}
	return errmsg.SUCCESS
}

// GetUsers 查询用户列表
func GetUsers(pageSize int, pageNum int) ([]User, int) {
	var users []User
	var total int
	//实现分页，防止一次性查询的数据过多导致数据库宕机
	Err = Db.Limit(pageSize).Offset((pageNum - 1) * pageSize).Find(&users).Count(&total).Error
	if Err != nil && Err != gorm.ErrRecordNotFound {
		return nil, 0
	}
	return users, total
}

// UpdateUser 更新用户 不包含修改密码  修改密码需要进行验证 可以单独写一个
func UpdateUser(id int, data *User) int {
	var user User
	var maps = make(map[string]interface{})
	//因为gorm中更新结构体类型数据时，这个Updates会不更新那些值为初始值，我们定义的role恰好是0和1，所以如果使用struct方法更新，则会更新失败
	//因此在这里定义了变量maps，采用map方法更新，解决role这个更新问题
	maps["username"] = data.Username
	maps["role"] = data.Role
	Err = Db.Model(&user).Where("id=?", id).Updates(maps).Error
	if Err != nil {
		return errmsg.ERROR
	}
	return errmsg.SUCCESS
}

// DelUser 删除用户
func DelUser(id int) int {
	Err = Db.Where("id=?", id).Delete(&User{}).Error //软删除
	if Err != nil {
		return errmsg.ERROR
	}
	return errmsg.SUCCESS
}

// ScryptPwd 密码加密
func ScryptPwd(password string) string {
	const PwdHashbyte = 10
	salt := make([]byte, 8)
	salt = []byte{15, 78, 32, 90, 59, 25, 32, 67}
	HashPwd, err := scrypt.Key([]byte(password), salt, 16384, 8, 1, PwdHashbyte)
	if err != nil {
		log.Fatal(err)
	}
	FPwd := base64.StdEncoding.EncodeToString(HashPwd)
	return FPwd //这是加密后的密码
}

//登录验证
func CheckLogin(username, password string) int {
	var user User
	Db.Where("username=?", username).First(&user)
	if user.ID == 0 {
		return errmsg.ErrorUserNotExists

	}
	if ScryptPwd(password) != user.Password {
		return errmsg.ErrorPwdWrong
	}
	if user.Role != 0 {
		return errmsg.ErrorUserNoAdmin
	}
	return errmsg.SUCCESS
}
